﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.ComponentModel;
using FRW.Common.Extensions;

namespace Money
{
    /// <summary>
    /// 
    /// </summary>
    public class Transactions : EventList<Transaction>
    {
        #region Events

        public event EventHandler<EventArgs> TransactionAdded;

        #endregion

        
        #region Fields

        private static Int64 _transactionNo;

        #endregion

        #region Contructors

        public Transactions()
        {
            // plumb in the event handlers that we want to use
            this.ItemAdded += Transactions_ItemAdded;
            this.BeforeItemAdded += Transactions_BeforeItemAdded;
        }

        #endregion

        #region Transaction status

        public void Saved()
        {
            foreach (Transaction transaction in this)
            {
                transaction.Saved();
            }
        }

        public bool IsDataDirty
        {
            get
            {
                var dirtyItems = from transaction in this where transaction.IsDirty == true select transaction;

                if (dirtyItems.Count() > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        #endregion

        #region Event Handlers

        void Transactions_BeforeItemAdded(object sender, ListPreEventArgs<Transaction> e)
        {
            Transaction transaction = e.Item;

            // TODO are there any checks we want to carry out before allowing the transaction to be added

            // check if this item has a transaction number, if not we need to add one
            if (transaction.TransactionNo == 0)
            {
                _transactionNo++;
                transaction.TransactionNo = _transactionNo;
            }
            else
            {
                if (transaction.TransactionNo > _transactionNo)
                {
                    _transactionNo = transaction.TransactionNo;
                }
            }
        }

        void Transactions_ItemAdded(object sender, ListEventArgs<Transaction> e)
        {
            // TODO we may want to limit this in cases where we are adding a range
            OnTransactionAdded();
        }

        #endregion

        #region Search Predicates

        // TODO we should now add a predicate that matches against a transaction match so that all comparissons are in the one place and simpler

        /// <summary>
        /// 
        /// </summary>
        /// <param name="accountName"></param>
        /// <returns></returns>
        public static Expression<Func<Transaction, bool>> Account(string accountName)
        {
            // check if we have a blank account name or certain patterns we will consider as matching all accounts
            if (String.IsNullOrWhiteSpace(accountName) || accountName == "[ALL]" || accountName == "ANY" || accountName == "ALL" || accountName == "[ANY]")
            {
                return t => true;
            }
            else
            {
                return t => t.Account.Equals(accountName);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static Expression<Func<Transaction, bool>> InDateRange(DateTime startDate, DateTime endDate)
        {
            if (startDate.Year > 2000 && endDate.Year > 2000)
            {
                return t => t.Date >= startDate && t.Date <= endDate;
            }
            else if (startDate.Year > 2000)
            {
                return t => t.Date >= startDate;
            }
            else if (endDate.Year > 2000)
            {
                return t => t.Date <= endDate;
            }
            else
            {
                return t => true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Expression<Func<Transaction, bool>> IsType(Transaction.TransactionType? type)
        {
            if (type != null)
            {
                return t => t.Type == type;
            }
            else
            {
                return t => true;
            }
        }

        public static Expression<Func<Transaction, bool>> IsAmountAbove(decimal amount)
        {
            return t => (t.Amount >= 0) ? t.Amount > amount : -t.Amount > amount;
        }

        public static Expression<Func<Transaction, bool>> IsEdited(bool? edited)
        {
            if (edited != null)
            {
                return t => t.IsDirty == edited;
            }
            else
            {
                return t => true;
            }
        }

        public static Expression<Func<Transaction, bool>> IsAmountBetween(decimal? min, decimal? max)
        {
            return t => ((min != null) ? Math.Abs(t.Amount) >= min : true) && ((max != null) ? Math.Abs(t.Amount) <= max : true);
        }

        public static Expression<Func<Transaction, bool>> IsMatch(TransactionMatchRule rule)
        {
            return t => (rule == null) ? true : rule.IsMatch(t);
        }

        public static Expression<Func<Transaction, bool>> IsSubCategory(string category)
        {
            if (String.IsNullOrWhiteSpace(category))
            {
                return t => true;
            }
            else
            {
                return t => t.Category.Equals(category);
            }
        }

        public static Expression<Func<Transaction, bool>> IsCategory(string categoryGroup)
        {
            if (String.IsNullOrWhiteSpace(categoryGroup))
            {
                return t => true;
            }
            else
            {
                return t => t.CategoryGroup.Equals(categoryGroup);
            }
        }

        public static Expression<Func<Transaction, bool>> IsDescription(string description)
        {
            if (String.IsNullOrWhiteSpace(description))
            {
                return t => true;
            }
            else
            {
                // TODO what comparisson type do we want and should we consider regular expression
                return t => t.CategoryGroup.Contains(description, StringComparison.CurrentCultureIgnoreCase);
            }
        }

        public static Expression<Func<Transaction, bool>> ContainsInDescription(string matchString)
        {
            if (String.IsNullOrWhiteSpace(matchString))
            {
                return t => true;
            }
            else
            {
                return t => t.Description.Contains(matchString, StringComparison.OrdinalIgnoreCase);
            }
        }

        #endregion

        #region Event Triggers

        public void OnTransactionAdded()
        {
            if (TransactionAdded != null)
            {
                TransactionAdded(this, new EventArgs());
            }
        }

        #endregion
    }
}
